package com.lin.nio.channel.block;

import com.lin.nio.utils.ByteBufferUtil;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;

/**
 * #### 阻塞网络编程(同步阻塞)
 * * 阻塞模式下，相关方法都会导致线程暂停
 *   * ServerSocketChannel.accept 会在没有连接建立时让线程暂停
 *   * SocketChannel.read 会在没有数据可读时让线程暂停
 *   * 阻塞的表现其实就是线程暂停了，暂停期间不会占用 cpu，但线程相当于闲置
 * * 单线程下，阻塞方法之间相互影响，几乎不能正常工作，需要多线程支持
 * * 但多线程下，一个连接用一个线程去处理，互不干扰，也有新的问题，体现在以下方面
 *   * 32 位 jvm 一个线程 320k，64 位 jvm 一个线程 1024k，如果连接数过多，必然导致 OOM，并且线程太多，反而会因为频繁上下文切换导致性能降低
 *   * 可以采用线程池技术来减少线程数和线程上下文切换，但治标不治本，如果有很多连接建立，但长时间 inactive，会阻塞线程池中所有线程，因此不适合长连接，只适合短连接
 */
public class TCPServer {
    public static void main(String[] args) {
        try {
            startServer();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private static void startServer() throws IOException {
        // 使用 nio 来理解阻塞模式, 单线程
        // 0. ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(16);
        // 1. 创建了TCP协议服务器
        ServerSocketChannel ssc = ServerSocketChannel.open();

        // 2. 绑定监听端口
        ssc.bind(new InetSocketAddress(8080));

        // 3. 连接集合
        List<SocketChannel> channels = new ArrayList<>();
        while (true) {
            // 4. accept 建立与客户端连接， SocketChannel 用来与客户端之间通信
            System.out.println("服务端等待连接...");
            SocketChannel sc = ssc.accept(); // 阻塞方法，线程停止运行，不往下执行了，直到有连接
            System.out.println("服务端收到连接：" +  sc);
            channels.add(sc);
            for (SocketChannel channel : channels) {
                // 5. 接收客户端发送的数据(监听客户端消息)
                System.out.println("收到客户端消息前");
                channel.read(buffer); // 阻塞方法，线程停止运行
                System.out.println("收到客户端消息后");
                ByteBufferUtil.debugAll(buffer);
                buffer.clear();
            }
        }
    }

    private void startClient() throws IOException {
        /// 创建一个SocketChannel
        SocketChannel sc = SocketChannel.open();
        sc.connect(new InetSocketAddress("localhost", 8080));
        System.out.println("waiting...");
    }

}
